/*
 * Copyright (C) 2011 The Android Open Source Project Copyright (C) 2011 Jake Wharton Licensed under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under
 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under the License.
 */
package com.viewpagerindicator;

import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;

import java.util.ArrayList;

import android.content.Context;
import android.graphics.drawable.ColorDrawable;
import android.os.Handler;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.text.Layout;
import android.util.AttributeSet;
import android.util.SparseIntArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.TextView;

import com.nineoldandroids.animation.ObjectAnimator;
import com.nineoldandroids.view.ViewHelper;

/**
 * This widget implements the dynamic action bar tab behavior that can change across different configurations or circumstances.
 */
public class TabPageIndicator extends LinearLayout implements PageIndicator, View.OnClickListener {

    /**
     * Title text used when no title is provided by the adapter.
     */
    private static final CharSequence EMPTY_TITLE = "";


    /**
     * Interface for a callback when the selected tab has been reselected.
     */
    public interface OnTabReselectedListener {

        /**
         * Callback when the selected tab has been reselected.
         *
         * @param position Position of the current center item.
         */
        void onTabReselected(int position);
    }

    private Runnable mTabSelector;

    private SparseIntArray mChildWidths = new SparseIntArray();

    private final IcsLinearLayout mTabLayout;

    private ViewPager mViewPager;
    private CustomHorizontalScrollView mScrollView;
    private View mExpandBtnLayer;
    private View mExpandBtnBg;
    private View mExpandBtn;
    private TextView mExpandTitle;
    private ExpandPopup mPopup;
    private static final long EXPANDABLE_THRESHOLD = 8;
    private static final int DEFAULT_ANIMATION_DURATION = 300;
    private boolean mIsExpand;


    private OnPageChangeListener mListener;

    private int mMaxTabWidth;
    private int mMaxTextWidth;
    private boolean mMatchParent = false; // 子控件的宽度是否能够填充满父控件
    private int mSelectedTabIndex;

    private boolean mIsExpandable;

    private OnTabReselectedListener mTabReselectedListener;


    private final OnClickListener mTabClickListener = new OnClickListener() {

        public void onClick(View view) {
            TabView tabView = (TabView) view;
            final int oldSelected = mViewPager.getCurrentItem();
            final int newSelected = tabView.getIndex();
            mViewPager.setCurrentItem(newSelected);
            if (oldSelected == newSelected && mTabReselectedListener != null) {
                mTabReselectedListener.onTabReselected(newSelected);
            }
        }
    };


    public TabPageIndicator(Context context) {
        this(context, null);
    }

    public TabPageIndicator(Context context, AttributeSet attrs) {
        super(context, attrs);

        LayoutInflater inflater = LayoutInflater.from(context);
        inflater.inflate(R.layout.vpi__expandable_tab_indicator, this, true);

        mScrollView = (CustomHorizontalScrollView) findViewById(R.id.tab_indicator);
        mTabLayout = new IcsLinearLayout(context, R.attr.vpiTabPageIndicatorStyle);
        mScrollView.addView(mTabLayout, new ViewGroup.LayoutParams(WRAP_CONTENT, MATCH_PARENT));
        mScrollView.setHorizontalScrollBarEnabled(false);

        mExpandBtn = findViewById(R.id.expand_btn);
        mExpandBtnLayer = findViewById(R.id.expand_btn_layer);
        mExpandBtnLayer.setOnClickListener(this);
        mExpandBtnBg = findViewById(R.id.expand_btn_bg);
        mExpandTitle = (TextView) findViewById(R.id.expand_title);
        ViewHelper.setAlpha(mExpandTitle, 0);
        mScrollView.setScrollToRightListener(new CustomHorizontalScrollView.OnScrollToRightListener() {
            @Override
            public void onScrollToRight(boolean isToRight) {
                if (isToRight) {
                    mExpandBtnBg.setVisibility(INVISIBLE);
                } else {
                    mExpandBtnBg.setVisibility(VISIBLE);
                }
            }
        });
    }

    public void setOnTabReselectedListener(OnTabReselectedListener listener) {
        mTabReselectedListener = listener;
    }

    public void setExpandable(boolean isExpandable) {
        this.mIsExpandable = isExpandable;
        if (isExpandable) {
            if (mPopup == null) {
                mPopup = new ExpandPopup(getContext());
            }
            mExpandBtnLayer.setVisibility(VISIBLE);
        } else {
            mExpandBtnLayer.setVisibility(GONE);
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged( w, h, oldw, oldh);
    }
    
    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        final boolean lockedExpanded = widthMode == MeasureSpec.EXACTLY;
        mScrollView.setFillViewport(lockedExpanded);

        final int childCount = mTabLayout.getChildCount();
        if (childCount > 1 && (widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST)) {
            float width = MeasureSpec.getSize(widthMeasureSpec);
            if (childCount > 2) {
                int size = mChildWidths.size();
                int totalWidth = 0;
                for (int i = 0; i < size; i++) {
                    totalWidth += mChildWidths.get(i);
                }
                if (totalWidth >= width) {
                    mMatchParent = true;
                } else {
                    mMatchParent = false;
                    if (mMaxTextWidth * childCount < width) {
                        mMaxTabWidth = (int) (width / childCount);
                    }
                }
            } else {
                mMaxTabWidth = (int) (width / 2);
            }
        } else {
            mMaxTabWidth = -1;
        }

        final int oldWidth = getMeasuredWidth();
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        final int newWidth = getMeasuredWidth();

        if (lockedExpanded && oldWidth != newWidth) {
            // Recenter the tab display if we're at a new (scrollable) size.
            setCurrentItem(mSelectedTabIndex);
        }
        ViewGroup.LayoutParams params = mExpandTitle.getLayoutParams();
        params.height = mScrollView.getMeasuredHeight();
        mExpandTitle.setLayoutParams(params);
        
        ViewGroup.LayoutParams layoutParams = (ViewGroup.LayoutParams)mExpandBtnBg.getLayoutParams();
        layoutParams.height = mScrollView.getMeasuredHeight();
        mExpandBtnBg.setLayoutParams( layoutParams);
    }

    private void animateToTab(final int position) {
        final View tabView = mTabLayout.getChildAt(position);
        if (mTabSelector != null) {
            removeCallbacks(mTabSelector);
        }
        mTabSelector = new Runnable() {

            public void run() {
                final int scrollPos = tabView.getLeft() - (getWidth() - tabView.getWidth()) / 2;
                mScrollView.smoothScrollTo(scrollPos, 0);
                mTabSelector = null;
            }
        };
        post(mTabSelector);
    }

    @Override
    public void onAttachedToWindow() {
        super.onAttachedToWindow();
        if (mTabSelector != null) {
            // Re-post the selector we saved
            post(mTabSelector);
        }
    }

    @Override
    public void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (mTabSelector != null) {
            removeCallbacks(mTabSelector);
        }
    }

    private void addTab(int index, CharSequence text, int iconResId) {
        final TabView tabView = new TabView(getContext());
        tabView.mIndex = index;
        tabView.setFocusable(true);
        tabView.setOnClickListener(mTabClickListener);
        tabView.setText(text);

        if (iconResId != 0) {
            tabView.setCompoundDrawablesWithIntrinsicBounds(iconResId, 0, 0, 0);
        }
        int width = (int) Layout.getDesiredWidth(text, tabView.getPaint());
        width = width + tabView.getPaddingLeft() + tabView.getPaddingRight();
        mChildWidths.put(index, width);
        mMaxTextWidth = Math.max(width, mMaxTextWidth);
        mTabLayout.addView(tabView, new LinearLayout.LayoutParams(0, MATCH_PARENT, 1));
        if (mPopup != null) {
            mPopup.addTab(text.toString());
        }
        // mTabLayout.setPadding( 0, 0, 60, 0);
    }

    public void setTabPadding(int left, int right) {
        mTabLayout.setPadding(left, 0, right, 0);
    }

    @Override
    public void onPageScrollStateChanged(int arg0) {
        if (mListener != null) {
            mListener.onPageScrollStateChanged(arg0);
        }
    }

    @Override
    public void onPageScrolled(int arg0, float arg1, int arg2) {
        if (mListener != null) {
            mListener.onPageScrolled(arg0, arg1, arg2);
        }
    }

    @Override
    public void onPageSelected(int arg0) {
        setCurrentItem(arg0);
        if (mListener != null) {
            mListener.onPageSelected(arg0);
        }
    }

    @Override
    public void setViewPager(ViewPager view) {
        if (mViewPager == view) {
            return;
        }
        if (mViewPager != null) {
            mViewPager.setOnPageChangeListener(null);
        }
        final PagerAdapter adapter = view.getAdapter();
        if (adapter == null) {
            throw new IllegalStateException("ViewPager does not have adapter instance.");
        }
        mViewPager = view;
        view.setOnPageChangeListener(this);
        notifyDataSetChanged();
    }

    public void notifyDataSetChanged() {
        mMaxTextWidth = 0;
        mTabLayout.removeAllViews();
        mChildWidths.clear();
        mMatchParent = false;
        PagerAdapter adapter = mViewPager.getAdapter();
        IconPagerAdapter iconAdapter = null;
        if (adapter instanceof IconPagerAdapter) {
            iconAdapter = (IconPagerAdapter) adapter;
        }
        final int count = adapter.getCount();
        if (count > EXPANDABLE_THRESHOLD) {
            mIsExpandable = true;
            if (mPopup == null) {
                mPopup = new ExpandPopup(getContext());
                mExpandBtnLayer.setVisibility(VISIBLE);
            }
        } else {
            mExpandBtnLayer.setVisibility(GONE);
        }
        for (int i = 0; i < count; i++) {
            CharSequence title = adapter.getPageTitle(i);
            if (title == null) {
                title = EMPTY_TITLE;
            }
            int iconResId = 0;
            if (iconAdapter != null) {
                iconResId = iconAdapter.getIconResId(i);
            }
            addTab(i, title, iconResId);
        }
        if (mSelectedTabIndex > count) {
            mSelectedTabIndex = count - 1;
        }
        setCurrentItem(mSelectedTabIndex);
        requestLayout();
    }

    @Override
    public void setViewPager(ViewPager view, int initialPosition) {
        setViewPager(view);
        setCurrentItem(initialPosition);
    }

    @Override
    public void setCurrentItem(int item) {
        if (mViewPager == null) {
            throw new IllegalStateException("ViewPager has not been bound.");
        }
        mSelectedTabIndex = item;
        mViewPager.setCurrentItem(item);
        final int tabCount = mTabLayout.getChildCount();
        for (int i = 0; i < tabCount; i++) {
            final TextView child = (TextView) mTabLayout.getChildAt(i);
            final boolean isSelected = (i == item);
            child.setSelected(isSelected);
            if (isSelected) {
                animateToTab(item);
            }
        }
    }

    @Override
    public void setOnPageChangeListener(OnPageChangeListener listener) {
        mListener = listener;
    }

    private class TabView extends TextView {

        private int mIndex;

        public TabView(Context context) {
            super(context, null, R.attr.vpiTabPageIndicatorStyle);
            setSingleLine(true);
        }

        @Override
        public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            int width = 0;
            if (mMatchParent) {
                width = mChildWidths.get(mIndex);
            } else {
                width = Math.max(mMaxTabWidth, mMaxTextWidth);
            }
            super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), heightMeasureSpec);
        }

        public int getIndex() {
            return mIndex;
        }
    }

    @Override
    public void onClick(View v) {
        if (v == mExpandBtnLayer) {
            mPopup.showAsDropDown(mScrollView, 0, 0);
        }
    }

    private boolean mIsAnimating;

    private class ExpandPopup extends PopupWindow implements AdapterView.OnItemClickListener {

        private GridView gridView;
        private ExpandTabAdapter adapter;

        public ExpandPopup(Context context) {
            super(context);

            LayoutInflater inflater = LayoutInflater.from(getContext());
            View view = inflater.inflate(R.layout.vpi__expandable_grid, null);
            setContentView(view);

            view.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    dismiss();
                }
            });

            gridView = (GridView) view.findViewById(R.id.expand_tabs);
            adapter = new ExpandTabAdapter();
            gridView.setAdapter(adapter);
            gridView.setOnItemClickListener(this);

            this.setWidth(android.view.ViewGroup.LayoutParams.MATCH_PARENT);
            this.setHeight(ViewGroup.LayoutParams.MATCH_PARENT);
            this.setFocusable(true);
            this.setAnimationStyle(R.style.popup_style);
            ColorDrawable drawable = new ColorDrawable(context.getResources().getColor(R.color.vpi__translucence));
            this.setBackgroundDrawable(drawable);
        }

        public void addTab(String tab) {
            adapter.addTab(tab);
        }

        @Override
        public void showAsDropDown(View anchor, int xoff, int yoff) {
            super.showAsDropDown(anchor, xoff, yoff);
            startShowAnimation();
        }

        @Override
        public void showAsDropDown(View anchor) {
            super.showAsDropDown(anchor);
            startShowAnimation();
        }

        @Override
        public void showAtLocation(View parent, int gravity, int x, int y) {
            super.showAtLocation(parent, gravity, x, y);
            startShowAnimation();
        }

        @Override
        public void dismiss() {
            if (mIsAnimating) {
                return;
            }
            mIsAnimating = true;
            AnimationSet animation = (AnimationSet) AnimationUtils.loadAnimation(getContext(), R.anim.vpi__push_up);
            animation.setFillAfter(true);
            gridView.clearAnimation();
            animation.setAnimationListener(new Animation.AnimationListener() {

                @Override
                public void onAnimationStart(Animation animation) {

                }

                @Override
                public void onAnimationRepeat(Animation animation) {

                }

                @Override
                public void onAnimationEnd(Animation animation) {
                    mIsAnimating = false;
                    mIsExpand = false;
                    new Handler().post(new Runnable() {

                        @Override
                        public void run() {
                            ExpandPopup.super.dismiss();
                        }
                    });
                }
            });
            gridView.setAnimation(animation);
            gridView.invalidate();
            changeIndicatorWithAnimation();
            animation.start();
        }

        void startShowAnimation() {
            if (mIsAnimating) {
                return;
            }
            Animation animation = AnimationUtils.loadAnimation(getContext(), R.anim.vpi__push_down);
            animation.setFillAfter(true);
            animation.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {

                }

                @Override
                public void onAnimationEnd(Animation animation) {
                    mIsExpand = true;
                    mIsAnimating = false;
                }

                @Override
                public void onAnimationRepeat(Animation animation) {

                }
            });
            gridView.setAnimation(animation);
            changeIndicatorWithAnimation();
            animation.start();
            mIsAnimating = true;
        }

        private void changeIndicatorWithAnimation() {
            if (mIsExpand) {
                ObjectAnimator alphaIn = ObjectAnimator.ofFloat(mScrollView, "alpha", 0, 1);
                alphaIn.setDuration(DEFAULT_ANIMATION_DURATION);
                alphaIn.start();

                ObjectAnimator alphaOut = ObjectAnimator.ofFloat(mExpandTitle, "alpha", 1, 0);
                alphaOut.setDuration(DEFAULT_ANIMATION_DURATION);
                alphaOut.start();

                ObjectAnimator rotate = ObjectAnimator.ofFloat(mExpandBtn, "rotation", 180, 0);
                rotate.setDuration(DEFAULT_ANIMATION_DURATION);
                rotate.start();

                ObjectAnimator alphaShow = ObjectAnimator.ofFloat(mExpandBtnBg, "alpha", 0, 1);
                alphaShow.setDuration(DEFAULT_ANIMATION_DURATION);
                alphaShow.start();
            } else {
                ObjectAnimator alphaIn = ObjectAnimator.ofFloat(mExpandTitle, "alpha", 0, 1);
                alphaIn.setDuration(DEFAULT_ANIMATION_DURATION);
                alphaIn.start();

                ObjectAnimator alphaOut = ObjectAnimator.ofFloat(mScrollView, "alpha", 1, 0);
                alphaOut.setDuration(DEFAULT_ANIMATION_DURATION);
                alphaOut.start();

                ObjectAnimator rotate = ObjectAnimator.ofFloat(mExpandBtn, "rotation", 0, 180);
                rotate.setDuration(DEFAULT_ANIMATION_DURATION);
                rotate.start();

                ObjectAnimator alphaGone = ObjectAnimator.ofFloat(mExpandBtnBg, "alpha", 1, 0);
                alphaGone.setDuration(DEFAULT_ANIMATION_DURATION);
                alphaGone.start();
            }
        }

        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            mViewPager.setCurrentItem(position, true);
            mPopup.dismiss();
        }

        private class ExpandTabAdapter extends BaseAdapter {

            private ArrayList<String> tabs = new ArrayList<String>();

            @Override
            public int getCount() {
                return tabs.size();
            }

            @Override
            public Object getItem(int i) {
                return tabs.get(i);
            }

            @Override
            public long getItemId(int i) {
                return i;
            }

            public void addTab(String tab) {
                tabs.add(tab);
            }

            @Override
            public View getView(int i, View convertView, ViewGroup viewGroup) {
                if (convertView == null) {
                    convertView = LayoutInflater.from(getContext()).inflate(R.layout.vpi__expandable_tab_item, null);
                }
                String title = tabs.get(i);
                TextView tabTitle = ViewHolder.get(convertView, R.id.tab_title);
                tabTitle.setText(title);
                if (i == mViewPager.getCurrentItem()) {
                    tabTitle.setSelected(true);
                } else {
                    tabTitle.setSelected(false);
                }

                return convertView;
            }
        }
    }
}
